package steveshrader.budget.client.widgets;

import java.util.Date;

import steveshrader.budget.client.FieldsHelper;
import steveshrader.budget.client.events.EditExpenseEvent;
import steveshrader.budget.client.events.ExpensesChangeEvent;
import steveshrader.budget.shared.BudgetHelper;
import steveshrader.budget.shared.BudgetRequestFactory;
import steveshrader.budget.shared.BudgetRequestFactory.ExpenseRequest;
import steveshrader.budget.shared.ExpenseProxy;

import com.google.gwt.cell.client.DateCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.OnlyToBeUsedInGeneratedCodeStringBlessedAsSafeHtml;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;

/**
 * A paging table with summaries of all expenses.
 */
public class ExpenseListWidget extends Composite {

  interface Binder extends UiBinder<Widget, ExpenseListWidget> {
  }

  interface Style extends CssResource {
  }

  interface TableResources extends CellTable.Resources {
    @Source(value = {CellTable.Style.DEFAULT_CSS, "CellTableStyle.css"})
    CellTable.Style cellTableStyle();
  }

  private class DateColumn extends Column<ExpenseProxy, Date> {
    public DateColumn() {
      super(new DateCell(FieldsHelper.DATE_TIME_FORMAT));
    }

    @Override
    public Date getValue(ExpenseProxy object) {
      return object.getDate();
    }
  }

  private class AmountColumn extends Column<ExpenseProxy, String> {
    public AmountColumn() {
      super(new TextCell());
    }

    @Override
    public String getValue(ExpenseProxy object) {
      return BudgetHelper.convertAmount(object.getAmount());
    }
  }

  private class VendorColumn extends Column<ExpenseProxy, String> {
    public VendorColumn() {
      super(new TextCell());
    }

    @Override
    public String getValue(ExpenseProxy object) {
      return object.getVendor();
    }
  }

  private class ExpenseTypeColumn extends Column<ExpenseProxy, String> {
    public ExpenseTypeColumn() {
      super(new TextCell());
    }

    @Override
    public String getValue(ExpenseProxy object) {
      return object.getExpenseType();
    }
  }

  private class PaymentTypeColumn extends Column<ExpenseProxy, String> {
    public PaymentTypeColumn() {
      super(new TextCell());
    }

    @Override
    public String getValue(ExpenseProxy object) {
      return object.getPaymentType();
    }
  }

  @UiField
  DockLayoutPanel dock;

  @UiField(provided = true)
  CellTable<ExpenseProxy> table;

  private final EventBus eventBus;
  private final BudgetRequestFactory requestFactory;  
  private final SingleSelectionModel<ExpenseProxy> selectionModel = new SingleSelectionModel<ExpenseProxy>();

  public ExpenseListWidget(EventBus eventBus, BudgetRequestFactory requestFactory) {
    this.eventBus = eventBus;
    this.requestFactory = requestFactory;
    
    table = new CellTable<ExpenseProxy>(15, GWT.<TableResources> create(TableResources.class));
    initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
    dock.getWidgetContainerElement(table).getStyle().setProperty("overflowY", "visible");

    Column<ExpenseProxy, Date> dateColumn = new DateColumn();
    table.addColumn(dateColumn, "Date");
    table.setColumnWidth(dateColumn, "18ex");
    Column<ExpenseProxy, String> amountColumn = new AmountColumn();
    amountColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LOCALE_END);
    table.addColumn(amountColumn, new SafeHtmlHeader(new OnlyToBeUsedInGeneratedCodeStringBlessedAsSafeHtml("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Amount")));  //cannot right align header on currency column so use spaces to force it right
    table.setColumnWidth(amountColumn, "25ex");
    Column<ExpenseProxy, String> vendorColumn = new VendorColumn();
    table.addColumn(vendorColumn, "Vendor");
    Column<ExpenseProxy, String> expenseTypeColumn = new ExpenseTypeColumn();
    table.addColumn(expenseTypeColumn, "Expense Type");
    table.setColumnWidth(expenseTypeColumn, "25ex");
    Column<ExpenseProxy, String> paymentTypeColumn = new PaymentTypeColumn();
    table.addColumn(paymentTypeColumn, "Payment Type");
    table.setColumnWidth(paymentTypeColumn, "25ex");    
    table.setSelectionModel(selectionModel);
    table.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);

    ExpensesChangeEvent.register(eventBus, new ExpensesChangeEvent.Handler() {
      public void onExpensesChanged(ExpensesChangeEvent e) {
    	  table.setRowData(e.getExpenses());
      }
    });

    selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
      public void onSelectionChange(SelectionChangeEvent event) {
    	ExpenseListWidget.this.refreshSelection();
      }
    });
  }

  @UiHandler("create")
  void onCreate(ClickEvent event) {
    ExpenseRequest context = requestFactory.expenseRequest();
    ExpenseProxy expense = context.edit(context.create(ExpenseProxy.class));
    expense.setDate(new Date());	//default to current date for a new Expense
    context.persist().using(expense);
    eventBus.fireEvent(new EditExpenseEvent(expense, context));
  }

  void refreshSelection() {
	ExpenseProxy expense = selectionModel.getSelectedObject();
    if (expense == null) {
      return;
    }
    eventBus.fireEvent(new EditExpenseEvent(expense));
    selectionModel.setSelected(expense, false);
  }
}
